////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#ifndef __WN_MATH_INTERNAL_ELEMENTS_H__
#define __WN_MATH_INTERNAL_ELEMENTS_H__

#include "WNCore/inc/WNAssert.h"
#include "WNCore/inc/WNExtendedTypes.h"
#include "WNCore/inc/WNTypeTraits.h"
#include "WNMath/inc/WNConfig.h"

namespace WNMath {
    namespace __WNInternal {
        template <typename Type, WN_UINT32 Dimension, typename = WNCore::WNEnableWhen<WN_TRUE>::Value>
        struct __WNElements {
            WN_STATIC_ASSERT_DESC(Dimension > 0, "Must have an element base of at least 1");

            Type mValues[Dimension];
        };

        template <typename Type, WN_UINT32 Dimension>
        class __WNElementsAddGeneric {
        public:
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element1, const __WNElements<Type, Dimension>& _element2);
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element, const Type& _number);

        private:
            __WNElementsAddGeneric() {}
        };

        template <typename Type, WN_UINT32 Dimension>
        class __WNElementsSubtractGeneric {
        public:
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element1, const __WNElements<Type, Dimension>& _element2);
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element, const Type& _number);

        private:
            __WNElementsSubtractGeneric() {}
        };

        template <typename Type, WN_UINT32 Dimension>
        class __WNElementsMultiplyGeneric {
        public:
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element1, const __WNElements<Type, Dimension>& _element2);
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element, const Type& _number);

        private:
            __WNElementsMultiplyGeneric() {}
        };

        template <typename Type, WN_UINT32 Dimension>
        class __WNElementsDivideGeneric {
        public:
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element1, const __WNElements<Type, Dimension>& _element2);
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element, const Type& _number);

        private:
            __WNElementsDivideGeneric() {}
        };

        template <typename Type, WN_UINT32 Dimension>
        class __WNElementsMultiplyAddGeneric {
        public:
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element1, const __WNElements<Type, Dimension>& _element2, const __WNElements<Type, Dimension>& _element3);
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element, const Type& _number1, const Type& _number2);

        private:
            __WNElementsMultiplyAddGeneric() {}
        };

        template <typename Type, WN_UINT32 Dimension>
        class __WNElementsMultiplySubtractGeneric {
        public:
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element1, const __WNElements<Type, Dimension>& _element2, const __WNElements<Type, Dimension>& _element3);
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element, const Type& _number1, const Type& _number2);

        private:
            __WNElementsMultiplySubtractGeneric() {}
        };

        template <typename Type, WN_UINT32 Dimension>
        class __WNElementsZeroGeneric {
        public:
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element);

        private:
            __WNElementsZeroGeneric() {}
        };

        template <typename Type, WN_UINT32 Dimension>
        class __WNElementsOneGeneric {
        public:
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element);

        private:
            __WNElementsOneGeneric() {}
        };

        template <typename Type, WN_UINT32 Dimension>
        class __WNElementsSetGeneric {
        public:
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element, const Type& _number);
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element, const Type* _numbers);

        private:
            __WNElementsSetGeneric() {}
        };

        template <typename Type, WN_UINT32 Dimension, typename = WNCore::WNEnableWhen<WN_TRUE>::Value>
        class __WNElementsAdd {
        public:
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element1, const __WNElements<Type, Dimension>& _element2);
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element, const Type& _number);

        private:
            __WNElementsAdd() {}
        };

        template <typename Type, WN_UINT32 Dimension, typename = WNCore::WNEnableWhen<WN_TRUE>::Value>
        class __WNElementsSubtract {
        public:
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element1, const __WNElements<Type, Dimension>& _element2);
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element, const Type& _number);

        private:
            __WNElementsSubtract() {}
        };

        template <typename Type, WN_UINT32 Dimension, typename = WNCore::WNEnableWhen<WN_TRUE>::Value>
        class __WNElementsMultiply {
        public:
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element1, const __WNElements<Type, Dimension>& _element2);
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element, const Type& _number);

        private:
            __WNElementsMultiply() {}
        };

        template <typename Type, WN_UINT32 Dimension, typename = WNCore::WNEnableWhen<WN_TRUE>::Value>
        class __WNElementsDivide {
        public:
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element1, const __WNElements<Type, Dimension>& _element2);
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element, const Type& _number);

        private:
            __WNElementsDivide() {}
        };

        template <typename Type, WN_UINT32 Dimension, typename = WNCore::WNEnableWhen<WN_TRUE>::Value>
        class __WNElementsMultiplyAdd {
        public:
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element1, const __WNElements<Type, Dimension>& _element2, const __WNElements<Type, Dimension>& _element3);
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element, const Type& _number1, const Type& _number2);

        private:
            __WNElementsMultiplyAdd() {}
        };

        template <typename Type, WN_UINT32 Dimension, typename = WNCore::WNEnableWhen<WN_TRUE>::Value>
        class __WNElementsMultiplySubtract {
        public:
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element1, const __WNElements<Type, Dimension>& _element2, const __WNElements<Type, Dimension>& _element3);
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element, const Type& _number1, const Type& _number2);

        private:
            __WNElementsMultiplySubtract() {}
        };

        template <typename Type, WN_UINT32 Dimension, typename = WNCore::WNEnableWhen<WN_TRUE>::Value>
        class __WNElementsZero {
        public:
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element);

        private:
            __WNElementsZero() {}
        };

        template <typename Type, WN_UINT32 Dimension, typename = WNCore::WNEnableWhen<WN_TRUE>::Value>
        class __WNElementsOne {
        public:
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element);

        private:
            __WNElementsOne() {}
        };

        template <typename Type, WN_UINT32 Dimension, typename = WNCore::WNEnableWhen<WN_TRUE>::Value>
        class __WNElementsSet {
        public:
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element, const Type& _number);
            static WN_FORCE_INLINE __WNElements<Type, Dimension>& Execute(__WNElements<Type, Dimension>& _element, const Type* _numbers);

        private:
            __WNElementsSet() {}
        };
    }
}

#ifdef __WN_MATH_ENABLE_EXTENSIONS
    #ifdef _WN_X86
        #include "WNMath/inc/Internal/x86/WNElements.h"
    #elif defined _WN_ARM
        #include "WNMath/inc/Internal/ARM/WNElements.h"
    #endif
#endif

#include "WNMath/inc/Internal/WNElements.inl"

#endif // __WN_MATH_INTERNAL_ELEMENTS_H__